// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.data.sources.feeds

import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import xyz.apiote.bimba.czwek.data.FeedInfoFlowItem
import xyz.apiote.bimba.czwek.data.exceptions.BimbaException
import xyz.apiote.bimba.czwek.repo.FeedInfo
import xyz.apiote.bimba.czwek.repo.QrLocation
import java.time.LocalDate
import java.time.format.DateTimeFormatter

class FeedInfoOfflineDataSource {
	companion object {
		const val KEY_ID = "id"
		const val KEY_NAME = "name"
		const val KEY_ATTR = "attr"
		const val KEY_DESC = "desc"
		const val KEY_LAST_UPDATE = "last_update"
		const val KEY_QR_HOST = "qr_host"
		const val KEY_QR_LOC = "qr_loc"
		const val KEY_QR_SEL = "qr_sel"
		const val KEY_VALID_S = "valid_since"
		const val KEY_VALID_T = "valid_till"
		const val KEY_IS_ONLINE = "is_online"
		const val KEY_IS_OFFLINE = "is_offline"
		const val KEY_FEED_INFOS = "feed_infos"

		private fun formatDate(date: LocalDate?): String {
			return date?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "NULL"
		}

		private fun parseDate(dateString: String): LocalDate? {
			return if (dateString == "NULL") {
				null
			} else {
				LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE)
			}
		}
	}

	fun saveFeedInfo(f: FeedInfo, context: Context) {
		context.getSharedPreferences(f.id, Context.MODE_PRIVATE).edit {
			putString(KEY_ID, f.id)
			putString(KEY_NAME, f.name)
			putString(KEY_ATTR, f.attribution)
			putString(KEY_DESC, f.description)
			putString(KEY_LAST_UPDATE, formatDate(f.lastUpdate))
			putString(KEY_QR_HOST, f.qrHost)
			putInt(KEY_QR_LOC, f.qrIn.value().toInt())
			putString(KEY_QR_SEL, f.qrSelector)
			putString(KEY_VALID_S, formatDate(f.validSince))
			putString(KEY_VALID_T, formatDate(f.validTill))
			putBoolean(KEY_IS_ONLINE, f.isOnline)
			putBoolean(KEY_IS_OFFLINE, f.isOffline)
		}

		val preferences = PreferenceManager.getDefaultSharedPreferences(context)
		val savedFeedInfos = preferences.getStringSet(KEY_FEED_INFOS, emptySet()) ?: emptySet()
		preferences.edit {
			putStringSet(KEY_FEED_INFOS, savedFeedInfos.plus(f.id))
		}
	}

	private fun getFeedInfo(id: String, context: Context): FeedInfo? {
		context.getSharedPreferences(id, Context.MODE_PRIVATE).let {
			it.getString(KEY_ID, null) ?: return null

			val name = it.getString(KEY_NAME, null)!!
			val attr = it.getString(KEY_ATTR, null)!!
			val desc = it.getString(KEY_DESC, null)!!
			val lastUpdate = parseDate(it.getString(KEY_LAST_UPDATE, null)!!)!!
			val qrHost = it.getString(KEY_QR_HOST, null)!!
			val qrLoc = QrLocation.of(it.getInt(KEY_QR_LOC, 0))
			val qrSel = it.getString(KEY_QR_SEL, null)!!
			val validSince = parseDate(it.getString(KEY_VALID_S, null) ?: "NULL")
			val validTill = parseDate(it.getString(KEY_VALID_T, null) ?: "NULL")
			val isOnline = it.getBoolean(KEY_IS_ONLINE, true)
			val isOffline = it.getBoolean(KEY_IS_OFFLINE, false)

			return FeedInfo(
				id,
				name,
				attr,
				desc,
				lastUpdate,
				qrHost,
				qrLoc,
				qrSel,
				validSince,
				validTill,
				true,
				isOnline,
				isOffline
			)
		}
	}

	private fun removeFeedInfo(id: String, context: Context) {
		val preferences = PreferenceManager.getDefaultSharedPreferences(context)
		val savedFeedInfos = preferences.getStringSet(
			KEY_FEED_INFOS, emptySet()
		) ?: emptySet()

		preferences.edit {
			putStringSet(KEY_FEED_INFOS, savedFeedInfos.minus(id))
		}

	}

	fun getFeedInfos(context: Context): Flow<FeedInfoFlowItem> = flow {
		val savedFeedInfos = PreferenceManager.getDefaultSharedPreferences(context).getStringSet(
			KEY_FEED_INFOS, emptySet()
		) ?: emptySet()

		savedFeedInfos.mapNotNull { id ->
			try {
				val feedInfo = getFeedInfo(id, context)
				if (feedInfo == null) {
					removeFeedInfo(id, context)
					null
				} else {
					feedInfo
				}
			} catch (e: BimbaException) {
				e
			}
		}.map { emit(it) }
	}
}